package haw.pr2.A2a;

import haw.pr2.A2a.JGameAdapter;
import haw.pr2.A2a.ParticleInterface;
import haw.pr2.A2a.interfaces.Speed;
import jgame.JGObject;
import static haw.pr2.A2a.implementations.UtilityclassValues.*;
import haw.pr2.A2a.interfaces.*;

/** Aufgabe 2a
 */
public class Aufgabe2a {

    public static void main(String[] args) {
        Aufgabe2a game = new Aufgabe2a();
        game.initGame();
    }

    private void initGame() {
        JGameAdapter.registerPartikle(new Porsche911GT2RS());
        JGameAdapter.startEngine(800, 600);
    }

    /** Our user-defined object. */
    class Porsche911GT2RS implements ParticleInterface {
        //Konstanten
        private final double NEUTRAL = 0.0;
        private final double EIS = 0.1;
        private final double SCHNEE = 0.3;
        private final double NAESSE = 0.7;
        private final double NORMAL = 1.0;
        private final double PORSCH_KW = 456.0;
        private final double MAXSPEED = 330.0;
        private final double WEIGHT = 1445.0;
        
        private final Power KW = powerInKw(PORSCH_KW);
        private final Speed maxSpeed = speedInKmH(MAXSPEED);
        private final Speed minSpeed = minSpeed();
        private final TimeDiff HOUR_IN_SEC = hourInSec();
        private final Mass mass = massInKg(WEIGHT); // [kg]
        private final Power powerPropMax = powerInW(KW.w()); // [ ??] [W]
        private final Speed speedMax = speedInMs(maxSpeed.mpS()); //km/h * 1000 /3600 = [ms^-1]
        private final Acc accEarth = accEarth(); // m/s^2
       
        private double underground=NORMAL;
        private Power forcePropMax = mass.mulA(accEarth.mul(underground));
        private final Length YPos = lengthInM(200.0); //Y-Position des Autos
        
        //Eigenschaften von Porsche
        private Speed speed = speedInMs(NEUTRAL); // m/s
        private double level = NEUTRAL;
        private Length pos = lengthInM(NEUTRAL); //Startposition
        private boolean abs = true;
        private boolean asr = true;
        private boolean baem = false;
        
        //Eigenschaften von Umwelt
        private TimeDiff time = timeDiffInS(NEUTRAL); //vergangene Zeit in Sekunden
        private Acc acc = accInMs(NEUTRAL); // in m/s^2

        /** Constructor. */
        Porsche911GT2RS() {
            reset();
        }

        private void reset() {
            this.baem = false;
            set(timeDiffInS(NEUTRAL), lengthInM(NEUTRAL), speedInMs(NEUTRAL), NEUTRAL);
        }

        /*
         * Setzt die aktuellen Werte
         */
        private void set(TimeDiff _time, Length _pos, Speed _speed, double _level) {
            Force force = forceInN(NEUTRAL);
            Force forceProp = forceInN(NEUTRAL);
            Force forceDrag = forceInN(NEUTRAL);
            Power powerProp = powerInKw(NEUTRAL);
            Force forcePropAbs = forceInN(NEUTRAL);
            Force forceDir = forceInN(NEUTRAL);
            final boolean fbreak = _level < NEUTRAL;
            
            // Set globals
            this.speed = speedInMs(_speed.mpS()); // m/s
            this.level = _level; // -1.0..+1.00
            this.pos = lengthInM(_pos.m()); // X-Position
            this.time = timeDiffInS(_time.s()); // in Sekunden

            powerProp = this.powerPropMax.mul(Math.abs(this.level)); // -1.0..+1.0 * W
            
            /** neue Berechnung
             * Wenn die Bremse betaetigt wird, dann wird die Bremskraft auf 
             * maximal 1G gesetzt, sonst ist die Kraft vorwaehrts gerichtet und 
             * entspricht der alten Berechnung
             */
      
            forceDir= fbreak ? this.mass.mulAtoF(this.accEarth.mul(Math.abs(this.level)))  : powerProp.divF(this.speed);
            
            /* ABS & ASR
             * Ist ABS aktiviert und es wird gebremst, oder ASR aktiviert und es wird nicht gebremst
             * So wird kontrolliert beschleunigt bzw. gebremst.
             */
            if ((abs && fbreak) || (asr && !fbreak) ){
                forcePropAbs = forceInN(Math.min(this.forcePropMax.kw(), forceDir.n()));
                
            /* Ist die Bremskraft groesser als der maximal zu uebertragenen Kraft
             * auf die Strasse, so wird ueberbremst! 
             */
            }else if ((forceDir.n() > this.forcePropMax.kw())) {
                this.baem = true;
                this.speed=speedInMs(NEUTRAL);
            /* Ist die Beschleunigung groesser als die maximal zu uebertragenen
             * Kraft auf die Strasse, so wird ueberbremst! 
             */
            }else {
                forcePropAbs=forceDir;
            }
          
            forcePropAbs = forceInN(Math.min(this.forcePropMax.kw(), forceDir.n())); // minimum( W ,  W / m/s )  
            
            forceProp = forcePropAbs.mul(Math.signum(this.level));
            
            forceDrag = speed.dragForce(speedMax, powerPropMax); // W / (m/s)^3 * ( (m/s)^2 * m/s ) = W
            
            force = forceProp.add(forceDrag); //  minimum ( W , W / (m/s))
            
            this.acc = force.divA(this.mass); // minimum ( W / KG , m/s )
        }

        /*
         * Führt einen Simulationsschritt aus
         */
        @Override
        public void step(TimeDiff deltaTime, double _level) {
            TimeDiff ltime; // in Sekunden
            Length lpos; // X-Position
            Speed lspeed; // m/s

            ltime = this.time.add(deltaTime); // s
            
            lpos = this.pos.add(this.speed.mulT(deltaTime));
            
            //Auto braucht eine Anfangsgeschwindigkeit um anfahren zu können
             lspeed = (this.speed.mpS() <= NEUTRAL) ? speedInMs(minSpeed.mpS()) : this.speed.add(this.acc.mulT(deltaTime)); // m/s

            //Auto zurücksetzen wenn es am Ende angekommen ist
            if (lpos.m() > 250) {
                lpos = lengthInM(0.0);
            }

            set(ltime, lpos, lspeed, _level);
        }
        
        
        
        @Override
        public double getXInMeters() {
            return this.pos.m();
        }

        @Override
        public double getYInMeters() {
            return this.YPos.m();
        }

        @Override
        public double getLevel() {
            return this.level;
        }

        @Override
        public double getSpeed() {
            return Math.round(this.speed.mpS() * 3.6 * 100d) / 100d; // km/h
        }

        @Override
        public double getTime() {
            return Math.round(this.time.mul(100d).s()) / 100d; //sek
        }

        @Override
        public void toggleAbs() {
            abs= (!abs);
        }

        @Override
        public void toggleAsr() {
            asr= (!asr);
        }

        @Override
        public boolean getAbsState() {
            return abs;
        }

        @Override
        public boolean getAsrState() {
            return asr;
        }

        @Override
        public boolean getExplState() {
            return baem;
        }

        @Override
        public void restGame() {
            reset();
        }

        @Override
        public void toggleUnderground() {
            if(this.underground == NORMAL) {
                this.underground = NAESSE;
            } else if (this.underground == NAESSE) {
                this.underground = SCHNEE;
            } else if (this.underground == SCHNEE){
                this.underground = EIS;
            } else {
                this.underground = NORMAL;
            }
            this.forcePropMax = mass.mulA(accEarth.mul(this.underground));        
        }

        @Override
        public String getUnderground() {
            if(this.underground == NORMAL) {
                return "Normal";
            } else if (this.underground == NAESSE) {
                return "Naesse";
            } else if (this.underground == SCHNEE){
                return "Schnee";
            } else if (this.underground == EIS) {
                return "Eis";
            } else {
                return "Unknown Underground";
            }
        }
        
    } /* end class Porsche911GT2RS */

}
